class Vocabluary
  attr_accessor :e, :f, :tbl

  def initialize english, french, val = nil
    self.e = english.uniq.sort
    self.f = french.uniq.sort
    val = Perplexity.to_perpelx_val(1.0/self.f.size) if val == nil
    self.tbl = (0..self.f.size-1).collect { |id|
      [val] * self.e.size
    }
  end

  def perplexity ej, fi
    return Perplexity.new self.tbl[self.f.index(fi)][self.e.index(ej)]
  end

  def set_perplexity ej, fi, p
    self.tbl[self.f.index(fi)][self.e.index(ej)] =
      (p.is_a? Perplexity) ? p.val : p
  end

  def add_perplexity ej, fi, p
    self.tbl[self.f.index(fi)][self.e.index(ej)] =
      (self.perplexity(ej, fi) +
       ((p.is_a? Perplexity) ? p : Perplexity.new(p))).val
  end

  def normalize
    self.tbl.each { |line|
      summ = Perplexity.inf
      line.each { |val|
        summ = summ + Perplexity.new(val)
      }
      (0..line.size-1).each { |id|
        line[id] = (Perplexity.new(line[id]) / summ).val
      }
    }
    self
  end

  def print_tbl
    self.tbl.each { |line|
      line = self.tbl[-1]
      str = ""
      line.each { |val|
#        str += Perplexity.new(val).to_i.to_s + " "
        str += val.to_s + " "
      }
      puts str
    }
  end

  def print_n_best n = 1
    res = self.tbl[0]
    (1..self.tbl.size-1).each { |f_id|
      res = res.zip self.tbl[f_id]
    }
    res = res.collect { |el| el.flatten }
    (0..res.size-1).each { |f_id|
      line = res[f_id]
      n_best = []
      worst = Perplexity.inf.val
      (0..line.size-1).each { |e_id|
        next if line[e_id] < worst
        n_best.push([e_id, line[e_id]]).sort! { |a, b|
          a[1] <=> b[1]
        }
        n_best.slice!(0..-(n+1))
        worst = n_best[0][1] if n_best.size == n
      }
      n_best.each { |b|
        puts self.e[f_id].to_s + " " + self.f[b[0]].to_s + " " + b[1].to_s
      }
    }
  end

  def self.test
    test_v = Vocabluary.new [:x, :y], [:b, :c]
    test_v.set_perplexity :x, :b, Perplexity.to_perpelx_val(1.0/2.0)
    test_v.set_perplexity :y, :b, Perplexity.to_perpelx_val(3.0/2.0)
    test_v.set_perplexity :x, :c, Perplexity.to_perpelx_val(1.0/2.0)
    test_v.set_perplexity :y, :c, Perplexity.to_perpelx_val(1.0/2.0)
    test_v.normalize
    raise "Vocabluary normalize failed" unless
      (test_v.perplexity(:x, :b).to_i - 1.0/4.0).abs < 0.0001 &&
      (test_v.perplexity(:y, :b).to_i - 3.0/4.0).abs < 0.0001 &&
      (test_v.perplexity(:x, :c).to_i - 1.0/2.0).abs < 0.0001 &&
      (test_v.perplexity(:y, :c).to_i - 1.0/2.0).abs < 0.0001
    res = [[1.0/4.0, 3.0/4.0],
           [1.0/2.0, 1.0/2.0]]
    test_v.tbl == res
  end
end
